package org.apogee.mil.client.main.pages.admin.forms;

import java.util.ArrayList;
import org.apogee.mil.client.factories.ServerServiceFactory;
import org.apogee.mil.client.utils.CDCUtils;
import org.apogee.mil.client.utils.Session;
import org.apogee.mil.shared.Constants.AccessLevelFields;
import org.apogee.mil.shared.Constants.DisplayFormat;
import org.apogee.mil.shared.User;
import org.apogee.mil.shared.Constants.UserLookupFields;
import org.apogee.mil.shared.Constants.UsersFields;

import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.data.BaseModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.CheckBox;
import com.extjs.gxt.ui.client.widget.form.ComboBox.TriggerAction;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.form.SimpleComboBox;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.grid.CellEditor;
import com.extjs.gxt.ui.client.widget.grid.CheckColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.grid.RowEditor;
import com.extjs.gxt.ui.client.widget.layout.FormData;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.googlecode.gwt.crypto.client.TripleDesCipher;

public class AdminUserForm extends LayoutContainer {

	private User currentUser;
	private FormData data;
	private ListStore<BaseModel> store = new ListStore<BaseModel>();
	private ColumnModel cm;
	private Grid<BaseModel> grid;
	private TripleDesCipher cipher;
	private TextField<Integer> userField;
	private SimpleComboBox<String> accessCombo;

	public AdminUserForm(){
		this.currentUser = ServerServiceFactory.getUser(Session.getSessionId());
		accessCombo = new SimpleComboBox<String>();
		getAccessLevels();

		cipher = new TripleDesCipher();
		cipher.setKey(CDCUtils.SECRET_KEY.getBytes());
	}


	public void onRender(Element parent, int index) {
		super.onRender(parent, index);


		data = new FormData("100%");
		FormPanel form = new FormPanel();
		form.setHeading("");  
		form.setFrame(true);
		form.setBorders(true);
		form.setSize(1000, 600);
		form.setBodyBorder(true);
		form.setHideLabels(true);
		form.setButtonAlign(HorizontalAlignment.CENTER);

		ServerServiceFactory.getAdminService(currentUser.getSessionId()).getUsers(currentUser.getAccessLevel(), new AsyncCallback<ArrayList<BaseModel>>() {

			@Override
			public void onSuccess(ArrayList<BaseModel> result) {
				if (result.size() == 0) {
					MessageBox.info("Users", "There were not users to display", null);
					return;
				}

				store.removeAll();
				store.add(result);
			}

			@Override
			public void onFailure(Throwable caught) {
				MessageBox.alert("Error", "An exception was thrown. Unable to retreive users. See logs for details", null);

			}
		});

		GridCellRenderer<BaseModel> renderer = new GridCellRenderer<BaseModel>() {

			@Override
			public Object render(BaseModel model, String property, ColumnData config,
					int rowIndex, int colIndex, ListStore<BaseModel> store,
					Grid<BaseModel> grid) {

				String val = (String) (model.get(property) == null ? "" : model.get(property));
				if (property.toLowerCase().trim().equals(UsersFields.PASSWORD)) {
					if (currentUser.getAccessLevel() > 0) {
						val = "************";
					} else {
						try {
							val = cipher.decrypt(val);
						} catch (Exception e) {
							val = "";
						} 
					}
				}
				return "<span qtitle='" + cm.getColumnById(property).getHeader() + "' qtip='" + val  
						+ "' style='font-weight: bold;color: #5d8aa8'>" + val + "</span>";
			}
		};


		ArrayList<ColumnConfig> configs = new ArrayList<ColumnConfig>();
		
		
		userField = new TextField<Integer>();
		userField.setEnabled(false);
		
		ColumnConfig userId= new ColumnConfig();
		userId.setId(UsersFields.USER_ID);
		userId.setHeader("User ID");
		userId.setWidth(100);
		userId.setRowHeader(true);
		userId.setRenderer(renderer);
		userId.setEditor(new CellEditor(userField));
		configs.add(userId);

		ColumnConfig column = new ColumnConfig();
		column.setId(UsersFields.USER_NAME);
		column.setHeader("User Name");
		column.setWidth(100);
		column.setRowHeader(true);
		column.setRenderer(renderer);
		column.setEditor(new CellEditor(new TextField<String>()));
		configs.add(column);

		column = new ColumnConfig();
		column.setId(UserLookupFields.FIRST_NAME);
		column.setHeader("First Name");
		column.setWidth(150);
		column.setRowHeader(true);
		column.setRenderer(renderer);
		column.setEditor(new CellEditor(new TextField<String>()));
		configs.add(column);

		column = new ColumnConfig();
		column.setId(UserLookupFields.LAST_NAME);
		column.setHeader("Last Name");
		column.setWidth(150);
		column.setRowHeader(true);
		column.setRenderer(renderer);
		column.setEditor(new CellEditor(new TextField<String>()));
		configs.add(column);


		final TextField<String> passwd = new TextField<String>();   
		passwd.setPassword(true);
		
		
		CellEditor pwdEditor = new CellEditor(passwd) {
			@Override
			public Object preProcessValue(Object value) {
				
				if (value == null)
					return null;

				String encryptedPassword = String.valueOf(value);
				String val = "";
				try {
					val = cipher.decrypt(encryptedPassword);
				} catch (Exception e) {
					val = "";
				} 
				return val;
			}

			@Override 
			public Object postProcessValue(Object value) {
				if (value == null)
					return value;

				try {
					return cipher.encrypt(value.toString().trim());
				} catch (Exception e) {

				} 
				return "";
			}
		};


		column = new ColumnConfig();
		column.setId(UsersFields.PASSWORD);
		column.setHeader("Password");
		column.setWidth(100);
		column.setRowHeader(true);
		column.setRenderer(renderer);
		column.setEditor(pwdEditor);
		configs.add(column);

		accessCombo.setForceSelection(true);  
		accessCombo.setTriggerAction(TriggerAction.ALL); 

		CellEditor editor = new CellEditor(accessCombo) {
			@Override
			public Object preProcessValue(Object value) {
				if (value == null) {  
					return value;  
				}  
				return accessCombo.findModel(value.toString());

			}

			@Override  
			public Object postProcessValue(Object value) {  
				if (value == null) {  
					return value;  
				}  

				String val = ((ModelData) value).get("value").toString().trim();

				try {
					int accessLevel = Integer.parseInt(val.substring(0,1));

					if (accessLevel < currentUser.getAccessLevel()) {
						MessageBox.alert("Error", "Unable to promote selected users access level higher than your own", null);
						return null;
					}
				} catch (Exception ex) {
					MessageBox.alert("Error", "NumberFormatException", null);
					return null;
				}

				return ((ModelData) value).get("value");  
			}  
		};


		column = new ColumnConfig();
		column.setId(DisplayFormat.ACCESS_FMT);
		column.setHeader("Access Level");
		column.setWidth(150);
		column.setRowHeader(true);
		column.setEditor(editor);
		configs.add(column);


		CheckColumnConfig checkColumn = new CheckColumnConfig("enabled", "Account Enabled", 100);
		CellEditor checkBoxEditor = new CellEditor(new CheckBox());
		checkColumn.setEditor(checkBoxEditor);
		configs.add(checkColumn);


		final RowEditor<BaseModel> re = new RowEditor<BaseModel>();


		form.setButtonAlign(HorizontalAlignment.CENTER);
		cm = new ColumnModel(configs);

		grid = new Grid<BaseModel>(store, cm);
		grid.setBorders(true);
		grid.setColumnLines(true);
		grid.getAriaSupport().setLabelledBy(form.getHeader().getId()+"-label");
		grid.setSize(300, 350);
		grid.addPlugin(re);
		
		re.addListener(Events.CancelEdit, new Listener<BaseEvent>() {

			@Override
			public void handleEvent(BaseEvent be) {
				userField.setEnabled(false);
				
			}
		});
		
		re.addListener(Events.AfterEdit, new Listener<BaseEvent>() {

			@Override
			public void handleEvent(BaseEvent be) {
				userField.setEnabled(false);
				
			}
		});
		

		ToolBar tb = new ToolBar();
		Button add = new Button("Add User");
		add.addSelectionListener(new SelectionListener<ButtonEvent>() {

			@Override
			public void componentSelected(ButtonEvent ce) {
				userField.setEnabled(true);
				BaseModel bm = new BaseModel();
				bm.set(DisplayFormat.NEW, DisplayFormat.NEW);
				re.stopEditing(false);
				store.insert(bm, store.getCount());
				re.startEditing(store.indexOf(bm), true);

			}
		});

		tb.add(add);

		form.setTopComponent(tb);
		
		
		form.add(grid, data);


		form.addButton(new Button("Reset", new SelectionListener<ButtonEvent>() {  

			@Override  
			public void componentSelected(ButtonEvent ce) {  
				store.rejectChanges();  
			}  
		}));  

		final ArrayList<BaseModel> changes = new ArrayList<BaseModel>();
		form.addButton(new Button("Save", new SelectionListener<ButtonEvent>() {  

			@Override  
			public void componentSelected(ButtonEvent ce) { 
				ArrayList<Record> changed = (ArrayList<Record>) store.getModifiedRecords();

				for (Record record : changed) {
					BaseModel bm = new BaseModel();
					for (String s : record.getPropertyNames()) {

						bm.set(s, record.get(s));
					}


					String accessLevelFmt = (String) bm.get(DisplayFormat.ACCESS_FMT);
					if (accessLevelFmt != null) {
						String id = accessLevelFmt.substring(0, 1);
						String desc = accessLevelFmt.substring(4);
						bm.set(AccessLevelFields.ID, id);
						bm.set(AccessLevelFields.DESCRIPTION, desc);
					}
					changes.add(bm);
				}

				store.commitChanges();
				
				ServerServiceFactory.getAdminService(currentUser.getSessionId()).updateUsers(changes, new AsyncCallback<Boolean>() {
					
					@Override
					public void onSuccess(Boolean result) {
						if (result == false) {
							MessageBox.alert("Error", "Unable to update users.", null);
						}
						
						Info.display("Success", "Users Updated Successfully");
						getParent().setVisible(false);
						
					}
					
					@Override
					public void onFailure(Throwable caught) {
						MessageBox.alert("Error", "Unable to update users.\n" + caught.getMessage(), null);
						
					}
				});

			}  
		}));


		add(form);

		layout();

	}

	private void getAccessLevels() {
		ServerServiceFactory.getAdminService(currentUser.getSessionId()).getAccessLevels(new AsyncCallback<ArrayList<BaseModel>>() {

			@Override
			public void onSuccess(ArrayList<BaseModel> result) {
				for (BaseModel bm : result) {
					accessCombo.add(bm.get(DisplayFormat.DESCRIPTION_FMT).toString());
					
				}
				accessCombo.repaint();

			}

			@Override
			public void onFailure(Throwable caught) {
				MessageBox.alert("Error", "Unable to get Access Levels:\n" + caught.getMessage(), null);
			}
		});
	}

}
